//
//  ViewController.m
//  IMSocket
//
//  Created by 崔逢举 on 2020/3/29.
//  Copyright ©  崔逢举. All rights reserved.
//

#import "ViewController.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>

#import "MsgCell.h"
#import "MsgModel.h"


//htons : 将一个无符号短整型的主机数值转换为网络字节顺序，不同cpu 是不同的顺序 (big-endian大尾顺序 , little-endian小尾顺序)
#define SocketPort htons(6969)
//inet_addr是一个计算机函数，功能是将一个点分十进制的IP转换成一个长整数型数
#define SocketIP   inet_addr("127.0.0.1")

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (weak, nonatomic) IBOutlet UITextField *textTF;

@property (nonatomic, assign) int client_socket;
@property (nonatomic, assign) int restartId;
@property (weak, nonatomic) IBOutlet UIBarButtonItem *connectItem;

@property (nonatomic, strong) NSMutableArray *dataArrM;

@property (nonatomic, assign) BOOL isClose;

@end

@implementation ViewController

#pragma mark --创建Socket连接
- (IBAction)socketConect:(UIBarButtonItem *)sender {
    //创建socket
    

     /**
     参数
    1. domain：协议域，又称协议族（family）。常用的协议族有AF_INET、AF_INET6、AF_LOCAL（或称AF_UNIX，Unix域Socket）、AF_ROUTE等。
    协议族决定了socket的地址类型，在通信中必须采用对应的地址，如AF_INET决定了要用ipv4地址（32位的）与端口号（16位的）的组合、AF_UNIX决定了要用一个绝对路径名作为地址。

    2. type：指定Socket类型。常用的socket类型有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。
    流式Socket（SOCK_STREAM）是一种面向连接的Socket，针对于面向连接的TCP服务应用。数据报式Socket（SOCK_DGRAM）是一种无连接的Socket，对应于无连接的UDP服务应用，常用的是SOCK_STREAM。

    3. protocol：指定协议。常用协议有IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等，分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

    注意：1.type和protocol不可以随意组合，如SOCK_STREAM不可以跟IPPROTO_UDP组合。当第三个参数为0时，会自动选择第二个参数类型对应的默认协议。

    返回值:如果调用成功就返回新创建的套接字的描述符，如果失败就返回INVALID_SOCKET（Linux下失败返回-1）
     */

    self.client_socket = socket(AF_INET, SOCK_STREAM, 0);
    
    if (self.client_socket == -1) {
        NSLog(@"---- socket创建失败 ------");
        return;
    }
    
    
    struct in_addr socketIn_addr;
    socketIn_addr.s_addr = SocketIP;
    
    struct sockaddr_in socketAddr;
    socketAddr.sin_family = AF_INET;
    socketAddr.sin_port = SocketPort;
    socketAddr.sin_addr = socketIn_addr;
  
    
    //连接
    
    /**
       参数
       参数一：套接字描述符
       参数二：指向数据结构sockaddr的指针，其中包括目的端口和IP地址
       参数三：参数二sockaddr的长度，可以通过sizeof（struct sockaddr）获得
       返回值
       成功则返回0，失败返回非0，错误码GetLastError()。
       */
    int result = connect(self.client_socket, (const struct sockaddr*)&socketAddr, sizeof(socketAddr));
    
    if (result != 0) {
        NSLog(@"---连接socket失败 -----");
        return;
    }
    
    sender.title = @"已连接";
    sender.enabled = NO;
    self.restartId = 0;
    
    //接受数据
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self receiveMsg];
    });
    
    
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.dataArrM = [NSMutableArray array];
}

#pragma mark --发送消息

- (IBAction)sendMsg:(UIButton *)sender {
    
    
    if (self.textTF.text.length == 0) {
        NSLog(@"消息为空");
        return;
    }
    const char *msg = self.textTF.text.UTF8String;
    
    ssize_t sendlength = send(self.client_socket, msg, strlen(msg), 0);
    NSLog(@"发送字节数:%ld",sendlength);
    
    MsgModel*model = [[MsgModel alloc]init];
    model.msg = self.textTF.text;
    model.isSend = YES;
    [self.dataArrM addObject:model];
    
    [self.tableView reloadData];
    
    self.textTF.text = @"";
    
}

#pragma mark --接受消息

- (void)receiveMsg {
    
    while (self.client_socket) {
        uint8_t buffer[1024];
        
        /*该函数的第一个参数指定接收端套接字描述符；

        第二个参数指明一个缓冲区，该缓冲区用来存放recv函数接收到的数据；

        第三个参数指明buf的长度； 第四个参数一般置0。
        */
        ssize_t receiveLength = recv(self.client_socket, buffer, sizeof(buffer), 0);
        
        if (receiveLength == 0) {
            self.restartId ++;
            if (self.restartId > 3) {
                self.restartId = 0;
                return;
            }
            NSLog(@"========= 数据为空，请检查连接 ========");
        }
        if (receiveLength == -1) {
            int close_result =  close(self.client_socket);
            
            if (close_result == -1) {
                NSLog(@"====  断开失败 ======");
            } else {
                self.connectItem.enabled = YES;
                self.connectItem.title = @"sokect连接";
                NSLog(@"====  断开成功 ======");
                self.client_socket = -1;

            }
            return;
        }
        
        //将接受到的数据进行转换
        NSData*recvData = [NSData dataWithBytes:buffer length:receiveLength];
        NSString*recvStr = [[NSString alloc]initWithData:recvData encoding:NSUTF8StringEncoding];
        NSLog(@"==接收到的数据为:%@",recvStr);
        self.restartId = 0;
        
        dispatch_async(dispatch_get_main_queue(), ^{
            
            MsgModel*model = [[MsgModel alloc]init];
            model.msg = recvStr;
            model.isSend = NO;
            [self.dataArrM addObject:model];
            
            [self.tableView reloadData];
            
        });
    }
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.dataArrM.count;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 44;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    MsgCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([MsgCell class])];
    if (cell == nil) {
        cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([MsgCell class]) owner:nil options:nil] firstObject];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }
    
    cell.model = self.dataArrM[indexPath.row];
    
    return cell;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    
    return [UIView new];
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    
    return 10;
}

- (UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
    
    return [UIView new];
}


- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    
    return CGFLOAT_MIN;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
}

#pragma mark -- 停止滚动的代理
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    
}



@end
